package cn.bugstack.mybatis.builder.xml;

import cn.bugstack.mybatis.builder.BaseBuilder;
import cn.bugstack.mybatis.builder.MapperBuilderAssistant;
import cn.bugstack.mybatis.executor.keygen.Jdbc3KeyGenerator;
import cn.bugstack.mybatis.executor.keygen.KeyGenerator;
import cn.bugstack.mybatis.executor.keygen.NoKeyGenerator;
import cn.bugstack.mybatis.executor.keygen.SelectKeyGenerator;
import cn.bugstack.mybatis.mapping.MappedStatement;
import cn.bugstack.mybatis.mapping.SqlCommandType;
import cn.bugstack.mybatis.mapping.SqlSource;
import cn.bugstack.mybatis.scripting.LanguageDriver;
import cn.bugstack.mybatis.session.Configuration;

import org.dom4j.Element;

import java.util.List;
import java.util.Locale;

/**
 *    <select id="selectBlog" parameterType="int" resultType="Blog">
 *      select * from Blog where id = #{id}
 *    </select>
 */
public class XmlStatementBuilder extends BaseBuilder {
    private Element element;

    private MapperBuilderAssistant builderAssistant;

    public XmlStatementBuilder(Configuration configuration, Element element, MapperBuilderAssistant builderAssistant) {
        super(configuration);
        this.element = element;
        this.builderAssistant = builderAssistant;
    }

    /**
     *   <select id="selectBlog" parameterType="int" resultType="Blog">
     *     select * from Blog where id = #{id}
     *   </select>
     *
     * @throws Exception
     */
    public void parseStatementNode() throws Exception{
        String id = element.attributeValue("id"); // queryUserInfo
        String parameterType = element.attributeValue("parameterType");
        Class<?> parameterTypeClass = resolveAlias(parameterType);

        String resultType = element.attributeValue("resultType");
        Class<?> resultTypeClass = resolveAlias(resultType);

        // 外部应用 resultMap
        String resultMap = element.attributeValue("resultMap");

        // select语句唯一标识


        // 获取sql的类型 insert | select | update | delete
        String nodeName = element.getName();
        SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));

        // 获取默认的语言驱动器
        LanguageDriver defaultDriver = configuration.getLanguageRegistry().getDefaultDriver();

        // 解析<selectKey> 存入configuration
        processSelectKeyNodes(id, parameterTypeClass, defaultDriver);

        SqlSource sqlSource = defaultDriver.createSqlSource(configuration, element, parameterTypeClass);

        // 属性标记【仅对 insert 有用】, MyBatis 会通过 getGeneratedKeys 或者通过 insert 语句的 selectKey 子元素设置它的值 step-14 新增
        String keyProperty = element.attributeValue("keyProperty");

        KeyGenerator keyGenerator = null;
        String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX;
        keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, false);
        if (configuration.hasKeyGenerator(keyStatementId)) {
            keyGenerator = configuration.getKeyGenerator(keyStatementId);
        } else {
            // processSelectKeyNodes 没有解析到 keyGenerator
            keyGenerator = sqlCommandType.equals(SqlCommandType.INSERT) && configuration.isUseGeneratedKeys() ? new Jdbc3KeyGenerator() : new NoKeyGenerator();
        }

        // 将 keyGenerator 与 insert 关联
        builderAssistant.addMappedStatement(id,
                sqlSource, sqlCommandType, parameterTypeClass, resultMap,
                resultTypeClass, keyProperty, keyGenerator, defaultDriver);





    }

    /**
     *   <selectKey keyProperty="id" order="AFTER" resultType="long">
     *       SELECT LAST_INSERT_ID()
     *   </selectKey>
     * @param parentId
     * @param parameterTypeClass
     * @param defaultDriver
     */
    private void processSelectKeyNodes(String parentId, Class<?> parameterTypeClass, LanguageDriver defaultDriver) {

        List<Element> selectKeyNodes = element.elements("selectKey");
        for (Element selectKeyNode : selectKeyNodes) {
            String id = parentId + SelectKeyGenerator.SELECT_KEY_SUFFIX;

            String resultType = selectKeyNode.attributeValue("resultType");
            Class<?> resultTypeClass = resolveClass(resultType);

            boolean executeBefore = "BEFORE".equals(selectKeyNode.attributeValue("order", "after"));

            String keyProperty = selectKeyNode.attributeValue("keyProperty");

            //将selectKeyNode 中的sql 解析为对应的sqlSource
            SqlSource selectKeySqlSource = defaultDriver.createSqlSource(configuration, selectKeyNode, parameterTypeClass);
            SqlCommandType sqlCommandType = SqlCommandType.SELECT;

            builderAssistant.addMappedStatement(id,
                    selectKeySqlSource,
                    sqlCommandType,
                    parameterTypeClass,
                    null,
                    resultTypeClass,
                    keyProperty,
                    new NoKeyGenerator(),
                    defaultDriver);

            id = builderAssistant.applyCurrentNamespace(id, false);

            MappedStatement selectKeyMappedStatement = configuration.getMappedStatement(id);
            // 将 selectKeyMappedStatement 存放到configuration
            configuration.addKeyGenerator(id, new SelectKeyGenerator(selectKeyMappedStatement, executeBefore));

        }


    }


}
